Skip to content

feat(schema): column names on ClickHouse and a casing table option - #109

Merged
Makisuo merged 3 commits into
MapleTechLabs:mainfrom
artile:feat/name-mapping
Nov 2, 2026
Merged

Makisuo merged 3 commits into
MapleTechLabs:mainfrom
artile:feat/name-mapping

Conversation

@artile

@artile artile commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

A ClickHouse column cannot have a database name apart from its key, and no table can derive its column names from a casing rule. Postgres had PG.column(type, { name }) only.

Closes #87.
Part of #52.

Changes

  • CH.column(type, { name: "org_id" }), as PG.column has. The DDL (columns, ORDER BY, PRIMARY KEY, PARTITION BY, indexes, defaults), queries, inserts, ALTER TABLE ... UPDATE, lightweight deletes and the tenant scope write the database name. Rows decode under the key.
  • A casing table option, "snake_case" or "camelCase" (new Casing type on both entries), on CH.table and PG.table, managed and external. It names every column that gives no name. A column's own name wins.
  • The casing rule is drizzle-orm's, word for word (orgId and OrgId → org_id, HTTPStatus → http_status, address2 stays). So a schema drizzle-kit made with casing: "snake_case" keeps its names.
  • The names are fixed where the table is defined, on each column's sqlName. The builder sees only names, so decoding still reads the exact alias it wrote (the note in design/gap-review.md P2).
  • A ClickHouse materialized view whose target renames a column records a definition problem. ClickHouse fills the target by database name, and the view's SELECT names its output by key.
  • Docs: a "Column names" section in docs/tables-and-types.md (backed by a docs example test), docs/reference.md, and the gap review. One minor changeset.

Left out

  • Table @@map: the first argument of table is already the database name, and the variable is the TypeScript name. No table name is a key anywhere in the API. The docs now say so.
  • A global casing option: it would need state outside the table, and the builder must not know casing. A project can wrap table in its own function.
  • A materialized view into a target with renamed columns: refused with a problem, not rewritten. It can come later, if it is wanted.
  • Tinybird datasources: they map columns by JSON path, so no change.
  • effect-orm pull still writes the database names as keys. A --casing flag for pull can come later.
  • SQLite, MySQL and D1: not on upstream main.

Validation

  • bun run build: pass.
  • bun run typecheck: pass.
  • EFFECT_ORM_CLICKHOUSE_URL=http://127.0.0.1:8123 EFFECT_ORM_CLICKHOUSE_USER=maple EFFECT_ORM_CLICKHOUSE_PASSWORD=maple bunx vitest run --testTimeout=60000 --hookTimeout=60000: 62 files, 1047 tests passed, 0 skipped, on ClickHouse 26.8.2.7. The new tests/column-names.clickhouse.test.ts creates a cased table, a renamed column and a materialized view from their own DDL, and checks system.columns.
  • node scripts/check-doc-citations.mjs, node scripts/check-exports-documented.mjs, node scripts/check-doc-examples.mjs: pass.
  • bun scripts/check-package.ts: pass.
  • Upstream CI for fork PRs waits for a maintainer to approve the run.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

artile and others added 2 commits October 25, 2026 17:27
CH.column takes name, as PG.column does: DDL, keys, statements and the
tenant scope write the database name, and rows decode under the key. A
casing option (snake_case or camelCase, drizzle-orm's rule) names every
column that gives no name, on both dialects and on external tables. A
materialized view whose target renames a column records a problem.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 46 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5b533da0-2c31-4987-8dc7-edf7661f019c
Commits

Reviewing files that changed from the base of the PR and between b57faf1 and a6167e2.

Files selected for processing: 17
  • .changeset/column-names-and-casing.md
  • design/gap-review.md
  • docs/reference.md
  • docs/tables-and-types.md
  • src/ch/column-names.test.ts
  • src/clickhouse.ts
  • src/docs-examples.test.ts
  • src/kit/sqlite-generate.test.ts
  • src/pg/column-names.test.ts
  • src/postgres.ts
  • src/schema/casing.ts
  • src/schema/define.ts
  • src/schema/pg-define.ts
  • src/schema/sqlite-define.ts
  • src/sqlite.ts
  • src/sqlite/column-names.test.ts
  • tests/column-names.clickhouse.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Resolve the pg-define import conflict. main's SQLite table passed a typeOf
callback to externalTable, which this branch removed; SQLite tables now name
columns through namedColumnType and take the casing option (managed and
external). ClickHouse tables check that no two keys map to one database name,
as the Postgres and SQLite ones already do. Coverage: SQLite DDL, node:sqlite
round trip and collisions under casing, and SQLite migration generation with
cased and renamed columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Makisuo

Makisuo commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Merge-readiness pass (merge commit a6167e2, no rebase):

  • Merged current main. There was a textual conflict in the src/schema/pg-define.ts imports, which I resolved. The real conflict was semantic: main's SQLite table (feat(sqlite): table DDL — columns, keys, indexes (#50, reviewed) #120) passed the typeOf callback to externalTable, and this branch removes that parameter.
    • SQLite tables now name their columns through namedColumnType, like CH and PG.
    • SQLite.table accepts casing, for both managed and external tables. Casing is exported from /sqlite.
  • Bug fix: ClickHouse tables had no duplicate-name check. Under casing: "snake_case", orgId and OrgId (or a key plus another column's name: "org_id") both became org_id, and that only failed at CREATE TABLE. CH.table now records a definition problem through the shared checkDistinctNames, as PG and SQLite already did. That check is case-sensitive on CH and case-insensitive on SQLite.
  • New coverage:
    • src/sqlite/column-names.test.ts: cased and renamed names in the SQLite DDL (columns, PK, indexes), an external table, the collision problem, and a node:sqlite round trip. The round trip covers insert, onConflictDoUpdate on a cased target, returning, update, delete, the tenant scope, and checks the stored names via pragma_table_info.
    • src/kit/sqlite-generate.test.ts: generate writes the cased and renamed names in the first migration and in ADD COLUMN. The migrated schema matches a fresh one, and a re-run writes nothing.
    • src/ch/column-names.test.ts: the CH collision problem.
  • Docs (tables-and-types.md > Column names) and the changeset now cover SQLite and the collision rule.

Verification (local, ClickHouse 26.8):

  • bun run build, bun run typecheck: pass.
  • bun run test: 1449/1449 tests passed, plus the doc citation, export catalog and doc example checks.
  • Live tests/column-names.clickhouse.test.ts and tests/migrate.clickhouse.test.ts: 10/10 passed.
  • FUZZ_SCALE=0.3 bunx vitest run tests/fuzz: 269/269 passed.

CI run 41473310518: the ClickHouse 26.2 job passed. The 26.8 job failed only on generated fixtures: query results agree (clickhouse) with ClickHouse "Division by zero", the known flake that #131 fixes.

@Makisuo
Makisuo merged commit 4069451 into MapleTechLabs:main Nov 2, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Name mapping: TS keys and DB names (@map / @@map, casing)

2 participants